package com.suning.sawp.service.impl.precharge;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.net.util.Base64;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import com.google.gson.reflect.TypeToken;
import com.suning.framework.ump.client.SimpleMsg;
import com.suning.rsc.RscException;
import com.suning.rsc.dto.responsedto.MbfResponse;
import com.suning.rsc.httpservice.annotation.EsbEIHttpWired;
import com.suning.rsf.consumer.ServiceLocator;
import com.suning.sawp.constants.CacheKeyConstants;
import com.suning.sawp.constants.ConfigConstants;
import com.suning.sawp.constants.ErrorCodeConstants;
import com.suning.sawp.constants.LoggerConstants;
import com.suning.sawp.constants.PreChargeErrorConstants;
import com.suning.sawp.constants.PromotionConstants;
import com.suning.sawp.dto.common.QueryResult;
import com.suning.sawp.dto.common.ReturnMsg;
import com.suning.sawp.dto.customer.CustshopFrequency;
import com.suning.sawp.dto.esb.CrtPreChargeOrderRtn;
import com.suning.sawp.dto.esb.CrtPreChargeOrderRtn.PosReturnStatus;
import com.suning.sawp.dto.esb.LocateMemberInfo;
import com.suning.sawp.dto.esb.PosSendSMSRtn;
import com.suning.sawp.dto.esb.QryPrepaidCoupOrderReq;
import com.suning.sawp.dto.esb.QryPrepaidCoupOrderRsp;
import com.suning.sawp.dto.esb.QueryPrechargeLimitRtn;
import com.suning.sawp.dto.precharge.ChargeCashOrderInfo;
import com.suning.sawp.dto.precharge.ChargeOrderForPageRtn;
import com.suning.sawp.dto.precharge.ChargeOrderRtn;
import com.suning.sawp.dto.precharge.ChargeOrderRtn.OrderStatus;
import com.suning.sawp.dto.precharge.ChargeRule;
import com.suning.sawp.dto.precharge.ClerkChargeInfo;
import com.suning.sawp.dto.precharge.ClertChargeRecord;
import com.suning.sawp.dto.precharge.CommonRtn;
import com.suning.sawp.dto.precharge.PreChargeAct;
import com.suning.sawp.dto.precharge.PreChargeDetailRtn;
import com.suning.sawp.dto.precharge.PreChargeDetailRtn.RuleTypeConstants;
import com.suning.sawp.dto.precharge.PrechargeActRtn;
import com.suning.sawp.dto.precharge.PrechargeRemain;
import com.suning.sawp.dto.precharge.PrechargeStatis;
import com.suning.sawp.dto.precharge.StoreChargeRecord;
import com.suning.sawp.intf.dao.PreChargeDaoService;
import com.suning.sawp.intf.esb.MemberInfoService;
import com.suning.sawp.intf.esb.MiscPrepaidCouponService;
import com.suning.sawp.intf.esb.PosPreChargeService;
import com.suning.sawp.intf.esb.ShopFrequencyService;
import com.suning.sawp.intf.oa.OaService;
import com.suning.sawp.intf.precharge.PreChargeService;
import com.suning.sawp.intf.precharge.SequenceSerivce;
import com.suning.sawp.intf.store.StoreInfoService;
import com.suning.sawp.po.precharge.PreChargeOrder;
import com.suning.sawp.po.precharge.PrechargeActivity;
import com.suning.sawp.po.precharge.PrechargeRule;
import com.suning.sawp.po.store.StoreInfo;
import com.suning.sawp.service.util.AESUtils;
import com.suning.sawp.service.util.BeanUtil;
import com.suning.sawp.service.util.ConfigUtil;
import com.suning.sawp.service.util.DJStringUtils;
import com.suning.sawp.service.util.DateUtils;
import com.suning.sawp.service.util.ExcelUtils;
import com.suning.sawp.service.util.MD5Util;
import com.suning.sawp.service.util.RedisCacheUtils;
import com.suning.scp.intf.facade.pos.PrePaidCouponService;
import com.suning.scp.intf.facade.pos.dto.PrePaidCouponDetail;
import com.suning.scp.intf.facade.pos.dto.PrePaidCouponDetailsResp;
import com.suning.scp.intf.facade.pos.dto.PrePaidCouponQueryReq;
import com.suning.scp.intf.facade.pos.dto.PrePaidCouponSummary;
import com.suning.vgs.follow.remote.intf.StoreConsumerService;
import com.suning.vgs.follow.remote.intf.StoreManRemoteService;
import com.suning.vgs.follow.remote.store.ConsumerBehaviorDto;
import com.suning.vgs.follow.remote.store.ConsumerBehaviorType;

/**
 * 
 * 店+预充值接口实现类<br>
 * 〈功能详细描述〉
 * 
 * @author Wuxiao
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
@Service
public class PreChargeServiceImpl implements PreChargeService {

    @Resource
    MongoTemplate                    safeMongoTemplate;

    @Autowired
    PosPreChargeService              posPreChargeService;

    @Autowired
    SequenceSerivce                  sequenceSerivce;

    @Autowired
    MemberInfoService                memberInfoService;

    @Autowired
    StoreInfoService                 storeInfoService;

    @Autowired
    OaService                        oaService;

    @Autowired
    StoreManRemoteService            storeManRemoteService;

    @Autowired
    PreChargeDaoService              preChargeDaoService;

    @Autowired
    StoreConsumerService             storeConsumerService;

    @Value("#{settingConfig[clientSignKey]}")
    String                           clientSignKey;

    @Value("#{umpConfig[SMSChargeTemplate]}")
    String                           smsTemplate;

    @Value("#{umpConfig[NoStoreSMSTemplate]}")
    String                           noStoreSmsTemplate;

    @Autowired
    RedisCacheUtils                  cacheUtils;

    @Resource
    MongoTemplate                    mongoTemplate;

    private MiscPrepaidCouponService miscPrepaidCouponService;

    PrePaidCouponService             prePaidCouponService     = ServiceLocator.getService(
                                                                      PrePaidCouponService.class,
                                                                      "");

    private static final Logger      LOGGER                   = LoggerFactory
                                                                      .getLogger(LoggerConstants.SAWP_SERVICE);

    /**
     * 充值流水号前缀
     */
    private static final String      CHARGE_ORDER_SEQ_KEY     = "SAWP";

    private static final Integer     ONE_HUNDRED              = 100;

    private static final String      SUCCESS                  = "0";                                           // pos
                                                                                                                // 定金翻番
                                                                                                                // 成功标志

    private static final int         TWO                      = 2;

    private static final int         THREE                    = 3;

    private static final String      SUCCESS_Y                = "Y";

    private static final String      FAILED_N                 = "N";

    private static final String      END_TIME_SUFFIXES        = "235959";

    @Resource
    ShopFrequencyService             shopFrequencyService;

    /**
     * 默认短信剩余发送次数
     */
    private static final String      DEFAULT_REMAIN_SMS_TIMES = "2";

    private final SimpleDateFormat   sd                       = new SimpleDateFormat("yyyyMMdd");

    public MiscPrepaidCouponService getMiscPrepaidCouponService() {
        return miscPrepaidCouponService;
    }

    @EsbEIHttpWired
    public void setMiscPrepaidCouponService(MiscPrepaidCouponService miscPrepaidCouponService) {
        this.miscPrepaidCouponService = miscPrepaidCouponService;
    }

    @Override
    public ReturnMsg<PreChargeDetailRtn> queryStoreActDetail(String storeCode) {
        ReturnMsg<PreChargeDetailRtn> ret = new ReturnMsg<PreChargeDetailRtn>();
        ret.setRetFlag(ReturnMsg.SUCCESS);
        if (StringUtils.isBlank(storeCode)) {
            ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E001,
                    PreChargeErrorConstants.PRECHARGE_ERR_MAP);
            return ret;
        }
        PreChargeDetailRtn chargeRtn = cacheUtils.hget(CacheKeyConstants.PRECHARGE_ACT, storeCode,
                PreChargeDetailRtn.class);
        if (chargeRtn == null) {
            chargeRtn = new PreChargeDetailRtn();
            PrechargeActivity activity = preChargeDaoService.queryOnSaleActivity(storeCode);
            if (activity == null) {
                ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E001,
                        PreChargeErrorConstants.PRECHARGE_ERR_MAP);
                return ret;
            }
            List<PrechargeRule> preRuleList = preChargeDaoService.queryActRule(activity
                    .getPromotionId().intValue());
            if (CollectionUtils.isEmpty(preRuleList)) {
                ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E001,
                        PreChargeErrorConstants.PRECHARGE_ERR_MAP);
                return ret;
            }
            chargeRtn.setChargeType(activity.getRuleType());
            chargeRtn.setRuleCode(activity.getRuleCode());
            List<ChargeRule> ruleList = new ArrayList<ChargeRule>();
            for (PrechargeRule preRule : preRuleList) {
                ChargeRule rule = new ChargeRule();
                rule.setRuleBase(getYuan(preRule.getRuleBase()));
                rule.setCouponAmount(getYuan(preRule.getCouponAmount()));
                rule.setMaxcouponAmount(getYuan(preRule.getMaxCouponAmount()));
                rule.setRuleId(String.valueOf(preRule.getRuleId()));
                StringBuilder build = new StringBuilder("充" + rule.getRuleBase() + "元返"
                        + rule.getCouponAmount() + "元券");
                if (RuleTypeConstants.EACH.equals(activity.getRuleType())) {
                    build = new StringBuilder("每" + rule.getRuleBase() + "元返"
                            + rule.getCouponAmount() + "元券最高可返" + rule.getMaxcouponAmount() + "元券");
                }
                rule.setRuleDesc(build.toString());
                ruleList.add(rule);
            }
            chargeRtn.setRuleList(ruleList);
            cacheUtils.hsetWithMyExpireTime(CacheKeyConstants.PRECHARGE_ACT, storeCode, chargeRtn,
                    CacheKeyConstants.PRECHARGE_ACT_EXPIRE);
        }
        ret.setData(chargeRtn);
        return ret;
    }

    /**
     * 
     * 功能描述: 分转为元<br>
     * 〈功能详细描述〉
     * 
     * @param fen
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private String getYuan(Long fen) {
        String ret = "";
        if (fen != null) {
            BigDecimal fen1 = new BigDecimal(fen);
            ret = fen1.divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_EVEN).toString()
                    .replace(".00", "");
        }
        return ret;
    }

    @Override
    public ReturnMsg<ChargeOrderRtn> payChargeOrder(String telNo, String storeCode, String userId,
            String ruleCode, String amount, String clientSerialNo, String sign) {
        ReturnMsg<ChargeOrderRtn> ret = new ReturnMsg<ChargeOrderRtn>();
        ChargeOrderRtn data = new ChargeOrderRtn();
        // 校验入参
        if (!checkParam(telNo, storeCode, userId, ruleCode, amount, clientSerialNo)) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_ERR_E999);
            ret.setErrorMessage(PreChargeErrorConstants.CLIENT_ERR_MAP
                    .get(PreChargeErrorConstants.POS_ERR_E999));
            data.setSysMsg(PreChargeErrorConstants.PRECHARGE_ERR_MAP
                    .get(PreChargeErrorConstants.PRECHARGE_ERR_E003));
            ret.setData(data);
            return ret;
        }
        // 校验是否在充值时间内
        if (!validChargeTime()) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_ERR_E008);
            ret.setErrorMessage(PreChargeErrorConstants.CLIENT_ERR_MAP
                    .get(PreChargeErrorConstants.POS_ERR_E008));
            return ret;
        }
        // 获取店员请求充值的次数
        Integer reqTimes = getReqTimes(userId);
        // 校验是否达到需要验证登录信息次数 每天
        if (reqTimes >= Integer.parseInt(ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.NEED_VALID_TIMES, ConfigConstants.DEFAULT_VALID_TIMES))) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_ERR_E007);
            ret.setErrorMessage(PreChargeErrorConstants.CLIENT_ERR_MAP
                    .get(PreChargeErrorConstants.POS_ERR_E007));
            return ret;
        }
        cacheUtils.hset(CacheKeyConstants.CHARGE_REQ_TIMES, sd.format(new Date()) + userId,
                reqTimes + 1);
        // 校验签名
        boolean isSignPassed = checkSign(telNo, storeCode, ruleCode, amount, clientSerialNo, sign);
        if (!isSignPassed) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_ERR_E999);
            ret.setErrorMessage(PreChargeErrorConstants.CLIENT_ERR_MAP
                    .get(PreChargeErrorConstants.POS_ERR_E999));
            data.setSysMsg(PreChargeErrorConstants.PRECHARGE_ERR_MAP
                    .get(PreChargeErrorConstants.PRECHARGE_ERR_E888));
            ret.setData(data);
            return ret;
        }
        // 获取店员当天已经成功充值的次数
        Integer chargeTimes = getChargedTimes(userId);
        // 每天最大充值次数
        String maxChargeTimes = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.MAX_CHARGE_TIMES, ConfigConstants.DEFAULT_MAX_CHARGE_TIMES);
        // 校验是否达到最大充值次数 每天
        if (chargeTimes >= Integer.parseInt(maxChargeTimes)) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_ERR_E006);
            String errorMsg = PreChargeErrorConstants.CLIENT_ERR_MAP
                    .get(PreChargeErrorConstants.POS_ERR_E006).replace("oaAccountNo", userId)
                    .replace("maxChargeTimes", maxChargeTimes);
            ret.setErrorMessage(errorMsg);
            return ret;
        }
        ReturnMsg<PreChargeDetailRtn> actDetail = queryStoreActDetail(storeCode);
        if (ReturnMsg.FAIL.equals(actDetail.getRetFlag())) {
            ret.setError(actDetail.getErrorCode(), actDetail.getErrorMessage());
            return ret;
        }
        // 调用会员接口获取用户编码
        LocateMemberInfo memberInfo = memberInfoService.locateMemberByMobileNo(telNo);
        // 判断是否是融合会员
        if (memberInfo == null || StringUtils.isBlank(memberInfo.getCustNum())
                || StringUtils.isBlank(memberInfo.getCardNo())) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_ERR_E999);
            ret.setErrorMessage(PreChargeErrorConstants.CLIENT_ERR_MAP
                    .get(PreChargeErrorConstants.POS_ERR_E999));
            data.setSysMsg(PreChargeErrorConstants.PRECHARGE_ERR_MAP
                    .get(PreChargeErrorConstants.PRECHARGE_ERR_E005));
            ret.setData(data);
            return ret;
        }
        // 生成交易流水号
        String seq = sequenceSerivce.incrSequenceNumber(CHARGE_ORDER_SEQ_KEY);
        String serialNo = CHARGE_ORDER_SEQ_KEY + seq;
        PreChargeOrder order = new PreChargeOrder();
        // 会员编码
        order.setCustNo(memberInfo.getCustNum());
        // 店员工号
        order.setOaAccountNo(userId);
        order.setPayTime(new Date());
        // 手机号
        order.setTelNo(telNo);
        order.setStoreCode(storeCode);
        // 充值记录状态 未发券
        order.setStatus(OrderStatus.UNDO_SEND_COUPON);
        // 客户端唯一流水号 db唯一索引
        order.setClientSerialNo(clientSerialNo);
        // 服务端生成充值流水号
        order.setSerialNo(serialNo);
        // 充值规则
        order.setRuleCode(ruleCode);
        // 充值金额 单位：分
        order.setChargeAmount(new BigDecimal(amount).multiply(new BigDecimal(ONE_HUNDRED)));
        // 短信剩余发送次数(默认2次)
        order.setRemainSmsTimes(ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.MAX_SMS_TIMES, DEFAULT_REMAIN_SMS_TIMES));
        StoreInfo store = storeInfoService.queryStoreByCode(order.getStoreCode());
        if (store != null) {
            order.setRegionName(store.getRegionName());
            order.setRegionCode(store.getRegionCode());
            order.setStoreName(store.getStoreName());
        }
        // 创建充值记录
        try {
            safeMongoTemplate.save(order);
        } catch (DuplicateKeyException e) {
            LOGGER.error("payChargeOrder DuplicateKeyException", e);
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_ERR_E999);
            ret.setErrorMessage(PreChargeErrorConstants.CLIENT_ERR_MAP
                    .get(PreChargeErrorConstants.POS_ERR_E999));
            data.setSysMsg(PreChargeErrorConstants.PRECHARGE_ERR_MAP
                    .get(PreChargeErrorConstants.PRECHARGE_ERR_E004));
            ret.setData(data);
            return ret;
        }
        // 调用POS充值接口
        CrtPreChargeOrderRtn rtn = posPreChargeService.posPrechargeOrder(memberInfo.getCustNum(),
                storeCode, amount, userId, ruleCode, serialNo);
        // 更新充值记录
        ret = updateOrder(rtn, clientSerialNo);
        if (ReturnMsg.SUCCESS.equals(ret.getRetFlag())) {
            cacheUtils.hset(CacheKeyConstants.CHARGE_TIMES_DAY, sd.format(new Date()) + userId,
                    chargeTimes + 1);
            order.setOrderId(rtn.getRechargeNo());
            order.setCouponAmount(new BigDecimal(rtn.getCouponAmount()).multiply(new BigDecimal(
                    ONE_HUNDRED)));
            order.setCouponStartTime(rtn.getStartTime());
            order.setCouponEndTime(rtn.getEndTime());
            order.setVerifyCode(rtn.getVerifyCode());
            // 发送充值成功短信
            sendSMS(order);
            // 调用用户信息采集接口，记录用户行为
            try {
                ConsumerBehaviorDto behavior = new ConsumerBehaviorDto();
                behavior.setBehaviorName("充" + amount + "返" + rtn.getCouponAmount());
                behavior.setCustNo(memberInfo.getCustNum());
                behavior.setBehaviorTime(System.currentTimeMillis());
                behavior.setStaffId(userId);
                behavior.setPhoneNo(telNo);
                behavior.setBehaviorType(ConsumerBehaviorType.PRECHARGE);
                storeConsumerService.upsertConsumerBehavior(behavior);
            } catch (Exception e) {
                LOGGER.error("现金充值采集用户信息失败,工号:" + userId + ",规则编码:" + ruleCode + ",会员编码:"
                        + memberInfo.getCustNum(), e);
            }
        }
        return ret;
    }

    /**
     * 
     * 功能描述: 重新发送充值成功短信<br>
     * 〈功能详细描述〉
     * 
     * @param mobile
     * @param orderNo
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    @SuppressWarnings("rawtypes")
    public ReturnMsg sendMessage(String orderNo) {
        ReturnMsg returnMsg = new ReturnMsg();
        PreChargeOrder preChargeOrder = preChargeDaoService.queryOrderBySerialNo(orderNo);
        if (null != preChargeOrder) {
            boolean flag = sendSMS(preChargeOrder);
            if (flag) {
                returnMsg.setRetFlag(ReturnMsg.SUCCESS);
            } else {
                returnMsg.setRetFlag(ReturnMsg.FAIL);
            }
        } else {
            returnMsg.setRetFlag(ReturnMsg.FAIL);
        }
        return returnMsg;
    }

    /**
     * 
     * 功能描述: 是否达到需要校验登录的次数<br>
     * 〈功能详细描述〉
     * 
     * @param userId
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private Integer getReqTimes(String userId) {
        Integer validTimes = cacheUtils.hget(CacheKeyConstants.CHARGE_REQ_TIMES,
                sd.format(new Date()) + userId, Integer.class);
        return validTimes == null ? Integer.valueOf(0) : validTimes;
    }

    /**
     * 
     * 功能描述:获取当天成功充值次数 <br>
     * 〈功能详细描述〉
     * 
     * @param userId
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private Integer getChargedTimes(String userId) {
        Integer chargeTimes = cacheUtils.hget(CacheKeyConstants.CHARGE_TIMES_DAY,
                sd.format(new Date()) + userId, Integer.class);
        return chargeTimes == null ? Integer.valueOf(0) : chargeTimes;
    }

    /**
     * 
     * 功能描述: 根据POS充值接口返回的结果更新充值记录<br>
     * 〈功能详细描述〉
     * 
     * @param rtn
     * @param clientSerialNo
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private ReturnMsg<ChargeOrderRtn> updateOrder(CrtPreChargeOrderRtn rtn, String clientSerialNo) {
        ReturnMsg<ChargeOrderRtn> ret = new ReturnMsg<ChargeOrderRtn>();
        ChargeOrderRtn data = new ChargeOrderRtn();
        Query query = new Query();
        Criteria criteria = new Criteria();
        criteria.and(PreChargeOrder.Attributes.CLIENT_SERIAL_NO).is(clientSerialNo);
        query.addCriteria(criteria);
        Update update = new Update();
        if (PreChargeErrorConstants.POS_ERR_E666.equals(rtn.getReturnCode())
                || PreChargeErrorConstants.POS_ERR_E777.equals(rtn.getReturnCode())
                || PreChargeErrorConstants.POS_ERR_E888.equals(rtn.getReturnCode())) {
            // 掉POS接口异常
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setError(PreChargeErrorConstants.POS_ERR_E999);
            ret.setErrorMessage(PreChargeErrorConstants.CLIENT_ERR_MAP
                    .get(PreChargeErrorConstants.POS_ERR_E999));
            // 充值记录状态 发券异常
            update.set(PreChargeOrder.Attributes.STATUS, OrderStatus.EXCEPTION_SEND_COUPON);
            update.set(PreChargeOrder.Attributes.CHARGE_ERROR_MSG, rtn.getErrorMessage());
        } else if (PosReturnStatus.SUCCESS.equals(rtn.getReturnCode())) {
            ret.setRetFlag(ReturnMsg.SUCCESS);
            // 充值记录状态 已发券
            update.set(PreChargeOrder.Attributes.STATUS, OrderStatus.DONE_SEND_COUPON);
            update.set(PreChargeOrder.Attributes.ORDER_ID, rtn.getRechargeNo());
            // 返券金额 单位：分
            update.set(PreChargeOrder.Attributes.COUPON_AMOUNT,
                    new BigDecimal(rtn.getCouponAmount()).multiply(new BigDecimal(ONE_HUNDRED))
                            .intValue());
            update.set(PreChargeOrder.Attributes.COUPON_START_TIME, rtn.getStartTime());
            update.set(PreChargeOrder.Attributes.COUPON_END_TIME, rtn.getEndTime());
            update.set(PreChargeOrder.Attributes.VERIFY_CODE, rtn.getVerifyCode());
        } else {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(rtn.getReturnCode());
            ret.setErrorMessage(PreChargeErrorConstants.CLIENT_ERR_MAP.get(rtn.getReturnCode()));
            data.setSysMsg(rtn.getErrorMessage());
            ret.setData(data);
            // 充值记录状态 发券失败
            update.set(PreChargeOrder.Attributes.STATUS, OrderStatus.FAIL_SEND_COUPON);
            update.set(PreChargeOrder.Attributes.CHARGE_ERROR_MSG, rtn.getErrorMessage());
        }
        safeMongoTemplate.updateFirst(query, update, PreChargeOrder.class);
        return ret;
    }

    /**
     * 
     * 功能描述: 校验签名<br>
     * 〈功能详细描述〉
     * 
     * @param telNo
     * @param storeCode
     * @param userId
     * @param ruleCode
     * @param amount
     * @param clientSerialNo
     * @param sign
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private boolean checkSign(String telNo, String storeCode, String ruleCode, String amount,
            String clientSerialNo, String sign) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("telNo", telNo);
        paramMap.put("storeCode", storeCode);
        paramMap.put("ruleCode", ruleCode);
        paramMap.put("amount", amount);
        paramMap.put("serialNo", clientSerialNo);
        String retSign = MD5Util.getSignStr(paramMap, "utf-8", clientSignKey);
        return sign.equalsIgnoreCase(retSign);
    }

    private boolean checkParam(String... args) {
        for (String s : args) {
            if (StringUtils.isBlank(s)) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean validChargeTime() {
        String expireTimeBigin = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.CHARGE_EXPIRE_BEGIN, ConfigConstants.DEFAULT_EXPIRE_BEGIN).replace(
                ":", "");
        String expireTimeEnd = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.CHARGE_EXPIRE_END, ConfigConstants.DEFAULT_EXPIRE_END).replace(":",
                "");
        SimpleDateFormat sd = new SimpleDateFormat("HHmmss");
        String currentTime = sd.format(new Date());
        try {
            return !(Integer.parseInt(currentTime) >= Integer.parseInt(expireTimeBigin) && Integer
                    .parseInt(currentTime) <= Integer.parseInt(expireTimeEnd));
        } catch (Exception e) {
            LOGGER.error("校验充值时间异常:", e);
            return false;
        }
    }

    /**
     * 
     * 功能描述: 发送短信<br>
     * 〈功能详细描述〉
     * 
     * @param order
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private boolean sendSMS(PreChargeOrder order) {
        boolean flag = false;
        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> paramMap = BeanUtils.describe(order);
            String couponStartTime = order.getCouponStartTime();
            String couponEndTime = order.getCouponEndTime();
            StoreInfo store = storeInfoService.queryStoreByCode(order.getStoreCode());
            // 获取短信模板
            String content = "";
            if (store != null && StringUtils.isNotBlank(store.getStoreName())) {
                // 获取有门店的短信模板
                content = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                        ConfigConstants.SMS_TEMPLATE, smsTemplate);
                paramMap.put("storeName", store.getStoreName());
            } else {
                // 获取无门店的短信模板
                content = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                        ConfigConstants.SMS_TEMPLATE_NO_STORE, noStoreSmsTemplate);
            }
            paramMap.put("chargeAmount", order.getChargeAmount()
                    .divide(new BigDecimal(ONE_HUNDRED)));
            paramMap.put("couponAmount", order.getCouponAmount()
                    .divide(new BigDecimal(ONE_HUNDRED)));
            paramMap.put("startMonth", couponStartTime.substring(4, 6));
            paramMap.put("startDay", couponStartTime.substring(6, 8));
            paramMap.put("startYear", couponStartTime.substring(0, 4));
            paramMap.put("endMonth", couponEndTime.substring(4, 6));
            paramMap.put("endDay", couponEndTime.substring(6, 8));
            paramMap.put("endYear", couponEndTime.substring(0, 4));
            // 替换变量
            for (Entry<String, Object> e : paramMap.entrySet()) {
                content = content.replace("{" + e.getKey() + "}", String.valueOf(e.getValue()));
            }
            if (StringUtils.isNotBlank(content) && StringUtils.isNotBlank(order.getTelNo())) {
                flag = SimpleMsg.send(order.getTelNo(), content);
            }
        } catch (Exception e) {
            LOGGER.error("发送短信异常--手机号:" + order.getTelNo() + "订单号:" + order.getOrderId(), e);
        }
        return flag;
    }

    @Override
    public boolean validUserPwd(String userId, String pwd) {
        if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(pwd)) {
            try {
                if (oaService.checkOaPwd(userId, new String(Base64.decodeBase64(pwd), "UTF-8"))) {
                    cacheUtils.hset(CacheKeyConstants.CHARGE_REQ_TIMES, sd.format(new Date())
                            + userId, 0);
                    return true;
                }
            } catch (UnsupportedEncodingException e) {
                LOGGER.error("Base64解码失败:" + pwd, e);
            }
        }
        return false;
    }

    @Override
    public ReturnMsg<List<ChargeCashOrderInfo>> queryOrderList(String chargeDate, String employeeId) {
        ReturnMsg<List<ChargeCashOrderInfo>> retMsg = new ReturnMsg<List<ChargeCashOrderInfo>>();

        // 根据员工id获取门店编码
        String storeCode = storeManRemoteService.getStoreCodeByStaffId(employeeId);

        // 调用接口获取记录
        QryPrepaidCoupOrderReq req = new QryPrepaidCoupOrderReq();
        req.setCasherId(employeeId);
        req.setRechargeDate(chargeDate);
        req.setStoreCode(storeCode);
        try {
            MbfResponse mbfResponse = miscPrepaidCouponService.queryPrepaidCouponsOrder(req);
            QryPrepaidCoupOrderRsp response = (QryPrepaidCoupOrderRsp) mbfResponse.getOutput()
                    .getMbfBody(QryPrepaidCoupOrderRsp.class);

            if (StringUtils.equals(SUCCESS, response.getResult())) {
                // 根据订单号，查询手机号
                // 返回成功
                List<QryPrepaidCoupOrderRsp.OrderInfo> infoList = response.getOrderInfos();
                if (CollectionUtils.isNotEmpty(infoList)) {
                    List<ChargeCashOrderInfo> orderInfoList = new ArrayList<ChargeCashOrderInfo>();
                    List<String> custList = new ArrayList<String>();
                    for (QryPrepaidCoupOrderRsp.OrderInfo tmp : infoList) {
                        Query query = new Query();
                        Criteria criteria = new Criteria();
                        // 更改为根据流水号查询现金预充值订单信息 modify by 12061818
                        criteria.and(PreChargeOrder.Attributes.SERIAL_NO).is(tmp.getSwiftNo());
                        query.addCriteria(criteria);
                        PreChargeOrder preChargeOrder = mongoTemplate.findOne(query,
                                PreChargeOrder.class);

                        custList.add(preChargeOrder == null ? StringUtils.EMPTY : preChargeOrder
                                .getCustNo());
                        // 设置返回报文
                        ChargeCashOrderInfo orderInfo = new ChargeCashOrderInfo();
                        orderInfo.setMemberPortraitUrl(DJStringUtils.getMemberProtraitUrl(orderInfo
                                .getCustNo()));// 会员画像url
                        // 修改手机号返回为加密手机号 店+3.3.2需求 客户端需要打电话
                        orderInfo.setMobile(preChargeOrder == null ? StringUtils.EMPTY : AESUtils
                                .encrypt(preChargeOrder.getTelNo(), ConfigConstants.AES_KEY));
                        orderInfo.setEncryMobile(preChargeOrder == null ? StringUtils.EMPTY
                                : DJStringUtils.sensitiveHandleEx(preChargeOrder.getTelNo(), THREE,
                                        TWO));
                        // orderInfo.setCustNo(preChargeOrder == null ?
                        // StringUtils.EMPTY : AESUtils
                        // .Encrypt(preChargeOrder.getCustNo(),
                        // ConfigConstants.AES_KEY));
                        orderInfo.setCustNo(preChargeOrder == null ? StringUtils.EMPTY
                                : preChargeOrder.getCustNo());
                        orderInfo.setOrderDesc(ConfigUtil
                                .getConfig("/config/sawp/config", "orderDesc")
                                .replace(
                                        "rechargeAmount",
                                        (int) NumberUtils.toDouble(tmp.getRechargeAmount())
                                                + StringUtils.EMPTY)
                                .replace(
                                        "couponAmount",
                                        (int) NumberUtils.toDouble(tmp.getCouponAmount())
                                                + StringUtils.EMPTY));// 订单描述
                        orderInfo.setOrderNo(tmp.getRechargeNo());// 单号

                        orderInfoList.add(orderInfo);
                    }
                    // 批量更新首复购标记
                    batchUpdateDealType(orderInfoList, custList);
                    retMsg.setData(orderInfoList);
                }

            } else {
                // 返回失败
                retMsg.setRetFlag(PromotionConstants.FAILED);
                retMsg.setErrorCode(StringUtils.EMPTY);
                retMsg.setErrorMessage(response.getErrMessage());

            }

        } catch (RscException e) {
            LOGGER.error("queryPrepaidCouponsOrder exception", e);
            // 返回失败
            retMsg.setRetFlag(PromotionConstants.FAILED);
            retMsg.setErrorCode(PromotionConstants.ERROR_CODE_QUERY_ORDER);
            retMsg.setErrorMessage(PromotionConstants.ERROR_MSG_QUERY_ORDER);

        }

        return retMsg;
    }

    /**
     * 批量更新首复购标记
     * @param orderInfoList
     * @param custList
     */
    private void batchUpdateDealType(List<ChargeCashOrderInfo> orderInfoList, List<String> custList) {
        Map<String, String> frequencyList = shopFrequencyService.queryShopFrequency(custList);
        for (ChargeCashOrderInfo orderInfo : orderInfoList) {
            String frequency = frequencyList.get(orderInfo.getCustNo());
            CustshopFrequency custshopFrequency = DJStringUtils.getDealType(frequency);
            orderInfo.setDealTypeTxt(custshopFrequency.getDealTypeTxt());
            orderInfo.setDealType(custshopFrequency.getDealType());
            if (StringUtils.isNotBlank(orderInfo.getCustNo())) {
                orderInfo
                        .setCustNo(AESUtils.encrypt(orderInfo.getCustNo(), ConfigConstants.AES_KEY));
            } else {
                orderInfo.setCustNo("");
            }
        }
    }

    @Override
    public PosSendSMSRtn posSendSMS(String serialNo) {
        PosSendSMSRtn ret = new PosSendSMSRtn();
        // 获取每天最大发送短信次数限制
        String maxSendTimes = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.MAX_POS_SEND_TIMES, ConfigConstants.DEFAULT_MAX_POS_SEND_TIMES);
        // 从redis中取出当天已发送短信次数
        Integer hasSendTimes = cacheUtils.hget(CacheKeyConstants.POS_SENDSMS_TIMES_DAY,
                sd.format(new Date()), Integer.class);
        hasSendTimes = hasSendTimes == null ? Integer.valueOf(0) : hasSendTimes;
        if (hasSendTimes > Integer.parseInt(maxSendTimes)) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_SMS_ERR_E001);
            ret.setErrorMessage(PreChargeErrorConstants.POS_SMS_ERR_MAP
                    .get(PreChargeErrorConstants.POS_SMS_ERR_E001));
            return ret;
        }
        // 根据充值流水号查询充值订单信息
        Query query = new Query();
        Criteria criteria = new Criteria();
        criteria.and(PreChargeOrder.Attributes.SERIAL_NO).is(serialNo);
        query.addCriteria(criteria);
        PreChargeOrder order = mongoTemplate.findOne(query, PreChargeOrder.class);
        if (order == null) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_SMS_ERR_E002);
            ret.setErrorMessage(PreChargeErrorConstants.POS_SMS_ERR_MAP
                    .get(PreChargeErrorConstants.POS_SMS_ERR_E002));
            return ret;
        }
        // 判断充值订单状态是否是充值成功
        if (!OrderStatus.DONE_SEND_COUPON.equals(order.getStatus())) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_SMS_ERR_E003);
            ret.setErrorMessage(PreChargeErrorConstants.POS_SMS_ERR_MAP
                    .get(PreChargeErrorConstants.POS_SMS_ERR_E003));
            return ret;
        }
        cacheUtils.hset(CacheKeyConstants.POS_SENDSMS_TIMES_DAY, sd.format(new Date()),
                hasSendTimes + 1);
        // 发送短信
        boolean flag = sendSMS(order);
        if (flag) {
            ret.setRetFlag(ReturnMsg.SUCCESS);
        } else {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.POS_SMS_ERR_E004);
            ret.setErrorMessage(PreChargeErrorConstants.POS_SMS_ERR_MAP
                    .get(PreChargeErrorConstants.POS_SMS_ERR_E004));
        }
        return ret;
    }

    @Override
    public ReturnMsg<PrechargeRemain> queryPrechargeRemain(String storeCode, String couponAmount) {
        ReturnMsg<PrechargeRemain> ret = new ReturnMsg<PrechargeRemain>();
        PrechargeRemain data = new PrechargeRemain();
        if (StringUtils.isBlank(storeCode) || StringUtils.isBlank(couponAmount)) {
            ret.setRetFlag(ReturnMsg.FAIL);
            ret.setErrorCode(PreChargeErrorConstants.PRECHARGE_ERR_E003);
            ret.setErrorMessage(PreChargeErrorConstants.PRECHARGE_ERR_MAP
                    .get(PreChargeErrorConstants.PRECHARGE_ERR_E003));
            return ret;
        }
        StoreInfo store = storeInfoService.queryStoreByCode(storeCode);
        if (store != null) {
            QueryPrechargeLimitRtn queryRtn = posPreChargeService.posQueryPrechargeLimit(storeCode,
                    store.getSaleBranchCode());
            if (queryRtn != null
                    && PreChargeErrorConstants.QUREY_REMIAN_SUCCESS.equals(queryRtn.getErrFlag())) {
                ret.setRetFlag(ReturnMsg.SUCCESS);
                if (StringUtils.isNotBlank(queryRtn.getCouponLimit())) {
                    BigDecimal remainValue = new BigDecimal(queryRtn.getCouponLimit());
                    if (remainValue.compareTo(new BigDecimal(1000)) < 0) {
                        BigDecimal remainCount = remainValue.divideToIntegralValue(new BigDecimal(
                                couponAmount));
                        data.setRemainCouponCounts(remainCount.intValue() == 0 ? "库存紧张" : String
                                .valueOf(remainCount.intValue()));
                    }
                    ret.setData(data);
                }
            } else if (queryRtn != null
                    && PreChargeErrorConstants.QUREY_REMIAN_FAILED.equals(queryRtn.getErrFlag())) {
                ret.setRetFlag(ReturnMsg.FAIL);
                ret.setErrorMessage(queryRtn.getErrString());
            } else {
                ret.setRetFlag(ReturnMsg.FAIL);
                ret.setErrorCode(PreChargeErrorConstants.PRECHARGE_ERR_E999);
                ret.setErrorMessage(PreChargeErrorConstants.PRECHARGE_ERR_MAP
                        .get(PreChargeErrorConstants.PRECHARGE_ERR_E999));
            }
        }
        return ret;
    }

    @Override
    public Integer queryExportCount(String startTime, String endTime) {
        try {
            Date startDate = DateUtils.getDateByStr(startTime, DateUtils.DEFAULT_DATE_PATTERN);
            Date endDate = DateUtils.getDateByStr(endTime, DateUtils.DEFAULT_DATE_PATTERN);
            Criteria criteria = new Criteria();
            criteria.and(PreChargeOrder.Attributes.PAY_TIME).gt(startDate).lt(endDate);
            criteria.and(PreChargeOrder.Attributes.STATUS).is(OrderStatus.DONE_SEND_COUPON);
            Query query = new Query();
            query.addCriteria(criteria);
            Long count = mongoTemplate.count(query, PreChargeOrder.class);
            return count.intValue();
        } catch (Exception e) {
            LOGGER.error("查询导出现金记录数量，时间转换异常,startTime=" + startTime + ",endTime=" + endTime, e);
        }
        return null;
    }

    @Override
    public File exportRecordExcel(String startTime, String endTime) {
        Integer count = queryExportCount(startTime, endTime);
        String maxCount = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.MAX_EXPORT_COUNT, ConfigConstants.DEFAULT_MAX_EXPORT_COUNT);
        if (count == null || count > Integer.parseInt(maxCount)) {
            return null;
        }
        String pageLimit = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.EXPORT_COUNT_PAGE, ConfigConstants.DEFAULT_EXPORT_COUNT_PAGE);
        Integer pageSize = Integer.parseInt(pageLimit);
        int pages = (count - 1) / pageSize + 1;
        SXSSFWorkbook wb = new SXSSFWorkbook(pageSize);
        Sheet sh = wb.createSheet();
        int rowNum = 0;
        for (int i = 0; i < pages; i++) {
            List<PreChargeOrder> list = queryRecordExport(startTime, endTime, i * pageSize,
                    pageSize);
            if (CollectionUtils.isEmpty(list)) {
                continue;
            }
            if (rowNum == 0) {
                Row row = sh.createRow(rowNum++);
                // 定义表头
                String[] title = "日期,券名称,工号,门店编码,门店名称,发券大区".split(",");
                for (int k = 0; k < title.length; k++) {
                    Cell cell = row.createCell(k);
                    cell.setCellValue(title[k]);
                }
            }
            try {
                for (PreChargeOrder order : list) {
                    Row row = sh.createRow(rowNum++);
                    row.createCell(0).setCellValue(
                            order.getPayTime() == null ? "" : DateUtils.getDateStrByFormat(
                                    order.getPayTime(), DateUtils.DEFAULT_TIME_PATTERN));
                    row.createCell(1).setCellValue(
                            order.getChargeAmount() == null || order.getCouponAmount() == null ? ""
                                    : "充"
                                            + order.getChargeAmount().divideToIntegralValue(
                                                    new BigDecimal(100))
                                            + "返"
                                            + order.getCouponAmount().divideToIntegralValue(
                                                    new BigDecimal(100)));
                    row.createCell(2).setCellValue(
                            order.getOaAccountNo() == null ? "" : order.getOaAccountNo());
                    row.createCell(3).setCellValue(
                            order.getStoreCode() == null ? "" : order.getStoreCode());
                    row.createCell(4).setCellValue(
                            order.getStoreName() == null ? "" : order.getStoreName());
                    row.createCell(5).setCellValue(
                            order.getRegionName() == null ? "" : order.getRegionName());
                }
            } catch (Exception e) {
                LOGGER.error("生产execl失败" + e);
            }
        }
        FileOutputStream out = null;
        try {
            final File createTempFile = File.createTempFile("temp1", "temp1.xlsx");
            out = new FileOutputStream(createTempFile);
            wb.write(out);
            return createTempFile;
        } catch (Exception e) {
            LOGGER.error("生成导出文件失败.", e);
        } finally {
            if (null != out) {
                try {
                    out.close();
                } catch (IOException e) {
                    LOGGER.error("生成导出文件失败.", e);
                }
            }
            wb.dispose();
        }
        return null;
    }

    @Override
    public ReturnMsg<QueryResult<ChargeOrderForPageRtn>> queryOrderForPage(String startTimeStr,
            String endTimeStr, String regionCode, String storeCode, int startIndex, int pageSize,
            String activityName) {
        ReturnMsg<QueryResult<ChargeOrderForPageRtn>> rtn = new ReturnMsg<QueryResult<ChargeOrderForPageRtn>>();
        QueryResult<ChargeOrderForPageRtn> queryRtn = new QueryResult<ChargeOrderForPageRtn>();
        // 获取最大分页查询数量 如果超过最大默认值，取默认值
        Integer queryLimit = Integer.valueOf(ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.COUPON_RECORD_QUERY_LIMIT,
                ConfigConstants.DEFAULT_QUERY_COUPON_LIMIT));
        int newPageSize = pageSize > queryLimit ? queryLimit : pageSize;

        Date startTime = DateUtils.getDateByStr(startTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
        Date endTime = DateUtils.getDateByStr(endTimeStr, DateUtils.DEFAULT_DAY_PATTERN);

        Integer chargeAmount = null;
        Integer couponAmount = null;
        // 活动名称非空，判断活动名称是否符合充XX返XX，满XX返XX
        if (StringUtils.isNotBlank(activityName)) {
            if (activityName.startsWith("充") || activityName.startsWith("满")) {
                activityName = activityName.substring(1);
                String[] nums = activityName.split("返");
                if (null != nums && nums.length == 2 && NumberUtils.isNumber(nums[0])
                        && NumberUtils.isNumber(nums[1])) {
                    chargeAmount = Integer.parseInt(nums[0]) * 100;
                    couponAmount = Integer.parseInt(nums[1]) * 100;
                }
            } else {
                // 不满足，则提示活动不存在
                rtn.setError(ErrorCodeConstants.ERR_0021);
                return rtn;
            }
        }

        // int totalSize =
        // preChargeDaoService.countChargeOrderForPage(startTime, endTime,
        // regionCode, storeCode);
        List<ChargeOrderForPageRtn> chargeOrderList = preChargeDaoService.queryChargeOrderForPage(
                startTime, endTime, regionCode, storeCode, chargeAmount, couponAmount, startIndex,
                newPageSize);
        int totalSize = chargeOrderList.size();
        queryRtn.setTotalSize(totalSize);
        List<ChargeOrderForPageRtn> retList = new ArrayList<ChargeOrderForPageRtn>();
        int endIndex = startIndex + newPageSize;
        if (endIndex > totalSize) {
            endIndex = totalSize;
        }
        for (int i = startIndex; i < endIndex; i++) {
            retList.add(chargeOrderList.get(i));
        }
        // 转换充、返数值
        for (ChargeOrderForPageRtn pageRtn : retList) {
            int retChargeAmount = (int) Math.floor(pageRtn.getChargeAmount());
            int retCouponAmount = (int) Math.floor(pageRtn.getCouponAmount());
            String retActivityName = "充" + retChargeAmount / 100 + "返" + retCouponAmount / 100;
            pageRtn.setActivityName(retActivityName);
        }
        queryRtn.setRecords(retList);
        rtn.setData(queryRtn);
        return rtn;
    }

    private List<PreChargeOrder> queryRecordExport(String startTime, String endTime, Integer start,
            Integer limit) {
        List<PreChargeOrder> list = new ArrayList<PreChargeOrder>();
        try {
            Date startDate = DateUtils.getDateByStr(startTime, DateUtils.DEFAULT_DATE_PATTERN);
            Date endDate = DateUtils.getDateByStr(endTime, DateUtils.DEFAULT_DATE_PATTERN);
            Criteria criteria = new Criteria();
            criteria.and(PreChargeOrder.Attributes.PAY_TIME).gt(startDate).lt(endDate);
            criteria.and(PreChargeOrder.Attributes.STATUS).is(OrderStatus.DONE_SEND_COUPON);
            Query query = new Query();
            query.addCriteria(criteria);
            query.skip(start);
            query.limit(limit);
            // 排序参数 按照发送时间倒序
            query.with(new Sort(new Order(Direction.DESC, PreChargeOrder.Attributes.PAY_TIME)));
            list = mongoTemplate.find(query, PreChargeOrder.class);
        } catch (Exception e) {
            LOGGER.error("查询导出发券记录数量，时间转换异常,startTime=" + startTime + ",endTime=" + endTime, e);
        }
        return list;
    }

    @Override
    public Workbook exportPrechargeStat(String activityName, String startTimeStr,
            String endTimeStr, String regionCode, String storeCode) {
        Workbook workbook = null;
        try {
            Date startTime = DateUtils.getDateByStr(startTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
            Date endTime = DateUtils.getDateByStr(endTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
            String maxCount = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                    ConfigConstants.MAX_EXPORT_COUNT, ConfigConstants.DEFAULT_MAX_EXPORT_COUNT);
            List<ChargeOrderForPageRtn> chargeOrderList = preChargeDaoService
                    .queryChargeOrderForPage(startTime, endTime, regionCode, storeCode, null, null);
            if (chargeOrderList.size() <= Integer.parseInt(maxCount)) {
                // 生成EXCEL
                // 带生成的标题栏
                String[] titles = { "活动名称", "大区名称", "门店名称", "发券数量" };
                // 准备待导出的数据
                List<String[]> datas = new ArrayList<String[]>();
                for (ChargeOrderForPageRtn pageRtn : chargeOrderList) {
                    int chargeAmount = (int) Math.floor(pageRtn.getChargeAmount());
                    int couponAmount = (int) Math.floor(pageRtn.getCouponAmount());
                    String activityNameStr = "充" + chargeAmount / 100 + "返" + couponAmount / 100;
                    String[] data = new String[4];
                    data[0] = activityNameStr;
                    data[1] = pageRtn.getRegionName();
                    data[2] = pageRtn.getStoreName();
                    data[3] = String.valueOf(pageRtn.getCouponCount());
                    datas.add(data);
                }
                // 导出EXCEL
                workbook = ExcelUtils.createExcel(datas, titles);
            }
        } catch (Exception e) {
            LOGGER.error("异业券统计导出异常", e);
        }
        return workbook;
    }

    @Override
    public ReturnMsg<String> checkPrechargeStatExport(String activityName, String startTimeStr,
            String endTimeStr, String regionCode, String storeCode) {
        ReturnMsg<String> returnMsg = new ReturnMsg<String>();
        if (StringUtils.isBlank(startTimeStr) || StringUtils.isBlank(endTimeStr)
                || StringUtils.isBlank(regionCode)) {
            returnMsg.setError(ErrorCodeConstants.ERR_0001);
            return returnMsg;
        }
        Date startTime = DateUtils.getDateByStr(startTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
        Date endTime = DateUtils.getDateByStr(endTimeStr, DateUtils.DEFAULT_DAY_PATTERN);
        List<ChargeOrderForPageRtn> list = preChargeDaoService.queryChargeOrderForPage(startTime,
                endTime, regionCode, storeCode, null, null);
        String maxCount = ConfigUtil.getConfig(ConfigConstants.SAWP_CONFIG_PATH,
                ConfigConstants.MAX_EXPORT_COUNT, ConfigConstants.DEFAULT_MAX_EXPORT_COUNT);
        Integer exportSize = list == null ? 0 : list.size();
        if (exportSize > Integer.parseInt(maxCount)) {
            returnMsg.setError(ErrorCodeConstants.ERR_0006, maxCount);
        } else if (exportSize == 0) {
            returnMsg.setError(ErrorCodeConstants.ERR_0019);
        } else {
            returnMsg.setData(String.valueOf(list.size()));
        }
        return returnMsg;
    }

    @Override
    public ReturnMsg<List<PreChargeAct>> queryPrechargeActList(String storeCode) {
        ReturnMsg<List<PreChargeAct>> ret = new ReturnMsg<List<PreChargeAct>>();
        Type type = new TypeToken<List<PreChargeAct>>() {
        }.getType();
        List<PreChargeAct> actList = cacheUtils.hget(CacheKeyConstants.UNEXPIRE_ACT_LIST,
                storeCode, type);
        if (actList == null) {
            actList = new ArrayList<PreChargeAct>();
            PrechargeActivity queryCon = new PrechargeActivity();
            queryCon.setStoreCode(storeCode);
            queryCon.setInvalidDate(Integer.valueOf(DateUtils.getDateStrByFormat(new Date(),
                    DateUtils.YYYYMMDD_PATTERN)));
            List<PrechargeActivity> list = preChargeDaoService.queryAct(queryCon);
            if (CollectionUtils.isNotEmpty(list)) {
                for (PrechargeActivity act : list) {
                    PreChargeAct prechargeAct = new PreChargeAct();
                    String promoStartTime = act.getPromoStartTime().toString().substring(4, 8);
                    String promoEndTime = act.getPromoEndTime().toString().substring(4, 8);
                    StringBuilder sb = new StringBuilder();
                    sb.append(promoStartTime).append("-").append(promoEndTime).append("预充值锁定活动");
                    prechargeAct.setActName(sb.toString());
                    prechargeAct.setRuleCode(act.getRuleCode());
                    actList.add(prechargeAct);
                }
                cacheUtils.hsetWithMyExpireTime(CacheKeyConstants.UNEXPIRE_ACT_LIST, storeCode,
                        actList, CacheKeyConstants.PRECHARGE_ACT_EXPIRE);
            }
        }
        ret.setData(actList);
        return ret;
    }

    @Override
    public ReturnMsg<ClertChargeRecord> queryClerkChargeRecord(String ruleCode, String storeCode,
            Integer pageNo, Integer pageSize, String staffId) {
        ReturnMsg<ClertChargeRecord> ret = new ReturnMsg<ClertChargeRecord>();
        if (!checkParam(ruleCode, storeCode, staffId)) {
            ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E003,
                    PreChargeErrorConstants.PRECHARGE_ERR_MAP);
            return ret;
        }
        pageNo = pageNo == null ? 1 : pageNo;
        pageSize = pageSize == null ? 10 : pageSize;
        PrechargeActivity queryCon = new PrechargeActivity();
        queryCon.setStoreCode(storeCode);
        queryCon.setRuleCode(ruleCode);
        List<PrechargeActivity> actList = preChargeDaoService.queryAct(queryCon);
        if (CollectionUtils.isEmpty(actList)) {
            ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E006,
                    PreChargeErrorConstants.PRECHARGE_ERR_MAP);
            return ret;
        }
        PrechargeActivity act = actList.get(0);
        PrePaidCouponQueryReq req = new PrePaidCouponQueryReq();
        req.setCasherId(staffId);
        //scp 页码从0开始
        req.setPage(pageNo - 1);
        req.setPageSize(pageSize);
        req.setRuleCode(ruleCode);
        req.setStoreCode(storeCode);
        req.setChargeStartDate(DateUtils.getDateByStr(String.valueOf(act.getPromoStartTime()),
                DateUtils.YYYYMMDD_PATTERN));
        req.setChargeEndDate(DateUtils.getDateByStr(String.valueOf(act.getPromoEndTime())
                + END_TIME_SUFFIXES, DateUtils.YYYYMMDDHHMMSS_PATTERN));
        PrePaidCouponDetailsResp resp = null;
        try {
            LOGGER.debug("调用SCP查询店员预充值记录请求报文{}", req.toString());
            resp = prePaidCouponService.queryPrePaidCoupons(req);
            LOGGER.debug("调用SCP查询店员预充值记录响应报文{}", resp.toString());
        } catch (Exception e) {
            LOGGER.error("调用SCP查询店员预充值记录异常,请求参数:" + req.toString(), e);
        }
        if (resp != null && SUCCESS_Y.equals(resp.getResult())) {
            ClertChargeRecord chargeRecord = new ClertChargeRecord();
            chargeRecord.setPageNum(resp.getPageNumber());
            chargeRecord.setPrechargeNum(resp.getChargeTimes());
            chargeRecord.setSendNum(resp.getGranttedTimes());
            List<PrePaidCouponDetail> couponList = resp.getCouponList();
            List<ClerkChargeInfo> rows = new ArrayList<ClerkChargeInfo>();
            StoreInfo store = storeInfoService.queryStoreByCode(storeCode);
            String limit = "";
            if(store != null){
                limit = getReaminAmount(storeCode, store.getSaleBranchCode());
            }
            for (PrePaidCouponDetail detail : couponList) {
                ClerkChargeInfo chargeInfo = new ClerkChargeInfo();
                chargeInfo.setLockDate(DateUtils.getDateStrByFormat(detail.getChargeTime(),
                        DateUtils.DEFAULT_TIME_PATTERN));
                chargeInfo.setOrderId(detail.getRechargeNo());
                String couponStatus = detail.getCouponStatus() == null ? "" : detail
                        .getCouponStatus();
                if (ClerkChargeInfo.couponStatus.LOCK.equals(couponStatus)) {
                    String nowDate = DateUtils.getDateStrByFormat(new Date(),
                            DateUtils.YYYYMMDD_PATTERN);
                    couponStatus = ClerkChargeInfo.couponStatus.LOCK_UNEFFECTIVE;
                    if (Integer.valueOf(nowDate).intValue() >= act.getEffectiveDate().intValue()) {
                        if (store != null) {
                            if (StringUtils.isNotBlank(limit)
                                    && StringUtils.isNotBlank(detail.getCouponAmount())
                                    && new BigDecimal(limit).compareTo(new BigDecimal(detail
                                            .getCouponAmount())) > 0) {
                                couponStatus = ClerkChargeInfo.couponStatus.LOCK_EFFECTIVE;
                            }
                        }
                    }
                }
                chargeInfo.setStatus(Integer.valueOf(couponStatus));
                Query query = new Query();
                Criteria criteria = new Criteria();
                criteria.and(PreChargeOrder.Attributes.ORDER_ID).is(detail.getRechargeNo())
                        .and(PreChargeOrder.Attributes.STORE_CODE).is(storeCode);
                query.addCriteria(criteria);
                PreChargeOrder order = mongoTemplate.findOne(query, PreChargeOrder.class);
                StringBuilder sb = new StringBuilder();
                sb.append("充").append(detail.getRechargeAmount()).append("元返")
                        .append(detail.getCouponAmount()).append("元券");
                chargeInfo.setLockRule(sb.toString());
                chargeInfo.setTelNo(order == null ? StringUtils.EMPTY : AESUtils.encrypt(
                        order.getTelNo(), ConfigConstants.AES_KEY));
                rows.add(chargeInfo);
            }
            chargeRecord.setRows(rows);
            ret.setData(chargeRecord);
            ret.setRetFlag(ReturnMsg.SUCCESS);
        } else if (resp != null && FAILED_N.equals(resp.getResult())) {
            ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E007, resp.getReturnMsg());
        } else {
            ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E007,
                    PreChargeErrorConstants.PRECHARGE_ERR_MAP);
        }
        return ret;
    }

    private String getReaminAmount(String storeCode, String branchCode) {
        QueryPrechargeLimitRtn queryRtn = posPreChargeService.posQueryPrechargeLimit(storeCode,
                branchCode);
        if (queryRtn != null
                && PreChargeErrorConstants.QUREY_REMIAN_SUCCESS.equals(queryRtn.getErrFlag())) {
            return queryRtn.getCouponLimit();
        }
        return null;
    }

    @Override
    public ReturnMsg<StoreChargeRecord> queryStoreChargeRecord(Integer sortColumn,
            Integer sortType, String storeCode, Integer pageNo, Integer pageSize) {
        ReturnMsg<StoreChargeRecord> ret = new ReturnMsg<StoreChargeRecord>();
        if (!checkParam(storeCode)) {
            ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E003,
                    PreChargeErrorConstants.PRECHARGE_ERR_MAP);
            return ret;
        }
        StoreChargeRecord record = new StoreChargeRecord();
        PrechargeActivity queryCon = new PrechargeActivity();
        queryCon.setStoreCode(storeCode);
        queryCon.setUnequalStatus(PrechargeActivity.promoStatusType.END);
        queryCon.setUnequalOpType(PrechargeActivity.OperateType.ADMIN_OFFSALE);
        queryCon.setUnequalOpType1(PrechargeActivity.OperateType.POS);
        List<PrechargeActivity> actList = preChargeDaoService.queryAct(queryCon);
        if (CollectionUtils.isEmpty(actList)) {
            record.setActStatus(PrechargeActivity.promoStatusType.END);
        } else {
            record.setActStatus(actList.get(0).getPromoStatus());
        }
        pageNo = pageNo == null ? 1 : pageNo;
        pageSize = pageSize == null ? 10 : pageSize;
        try {
            PrePaidCouponSummary couponSummary = prePaidCouponService
                    .queryPrepaidCouponSummary(storeCode);
            LOGGER.debug("调用POS查询充值统计{}", couponSummary);
            if (couponSummary != null && SUCCESS_Y.equals(couponSummary.getResult())) {
                record.setCouponAmount(couponSummary.getCouponAmount().toString());
                record.setSendAmount(couponSummary.getGranttedAmount().toString());
                record.setPrechargeNum(couponSummary.getChargeTimes());
                StoreInfo store = storeInfoService.queryStoreByCode(storeCode);
                if (store != null) {
                    String couponLimit = getReaminAmount(storeCode, store.getSaleBranchCode());
                    if(StringUtils.isNotBlank(couponLimit)){
                        record.setCouponLimit(couponLimit);
                        BigDecimal gapAmount = (new BigDecimal(couponSummary.getCouponAmount()))
                                .subtract(new BigDecimal(couponSummary.getGranttedAmount())).subtract(
                                        new BigDecimal(couponLimit));
                        record.setPrechargeGap(gapAmount.compareTo(new BigDecimal(0)) < 0 ? "0"
                                : gapAmount.toString());
                    }

                }
            }
        } catch (Exception e) {
            LOGGER.error("调用SCP查询门店充值统计异常,门店编码:" + storeCode, e);
        }
        int count = preChargeDaoService.queryPrechargeStatisCount(storeCode);
        List<PrechargeStatis> list = preChargeDaoService.queryPrechargeStatis(storeCode, pageNo,
                pageSize, sortColumn, sortType);
        record.setChargeListSize(count);
        record.setChargeList(list);
        ret.setRetFlag(ReturnMsg.SUCCESS);
        ret.setData(record);
        return ret;
    }

    @Override
    public ReturnMsg<CommonRtn> updateActStatus(String storeCode, Integer status) {
        ReturnMsg<CommonRtn> ret = new ReturnMsg<CommonRtn>();
        if (!checkParam(storeCode) || status == null) {
            ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E003,
                    PreChargeErrorConstants.PRECHARGE_ERR_MAP);
            return ret;
        }
        Integer operateType = null;
        if (status == PrechargeActivity.promoStatusType.ONSALE) {
            operateType = PrechargeActivity.OperateType.MANUAL_ONSALE;
        } else if (status == PrechargeActivity.promoStatusType.OFFSALE) {
            operateType = PrechargeActivity.OperateType.MANUAL_OFFSALE;
        }
        preChargeDaoService.updateActStatus(storeCode, status, operateType);
        cacheUtils.clearCacheData(CacheKeyConstants.PRECHARGE_ACT, storeCode);
        ret.setRetFlag(ReturnMsg.SUCCESS);
        return ret;
    }

    @Override
    public ReturnMsg<QueryResult<PrechargeActRtn>> queryActListPage(String keyWord,
            String promoStart, String promoEnd, Integer pageNum, Integer pageSize) {
        pageNum = pageNum == null ? 1 : pageNum;
        pageSize = pageSize == null ? 10 : pageSize;
        ReturnMsg<QueryResult<PrechargeActRtn>> ret = new ReturnMsg<QueryResult<PrechargeActRtn>>();
        QueryResult<PrechargeActRtn> queryRet = new QueryResult<PrechargeActRtn>();
        int count = preChargeDaoService.queryActListPageCount(keyWord, promoStart, promoEnd);
        List<PrechargeActRtn> retList = new ArrayList<PrechargeActRtn>();
        if (count > 0) {
            List<PrechargeActivity> list = preChargeDaoService.queryActListPage(keyWord,
                    promoStart, promoEnd, pageNum, pageSize);
            for (PrechargeActivity act : list) {
                PrechargeActRtn rtn = new PrechargeActRtn();
                BeanUtil.copyProperties(rtn, act);
                List<PrechargeRule> ruleList = preChargeDaoService.queryActRule(act
                        .getPromotionId().intValue());
                rtn.setCouponName(getCouponName(ruleList, act.getRuleType()));
                retList.add(rtn);
            }
        }
        queryRet.setRecords(retList);
        queryRet.setTotalSize(count);
        ret.setRetFlag(ReturnMsg.SUCCESS);
        ret.setData(queryRet);
        return ret;
    }

    private String getCouponName(List<PrechargeRule> ruleList, String type) {
        StringBuilder sb = new StringBuilder();
        String preFix = "充";
        if (PrechargeActivity.rulesType.EACH.equals(type)) {
            preFix = "每";
        }
        for (PrechargeRule rule : ruleList) {
            sb.append(preFix).append(rule.getRuleBase() / 100).append("元返")
                    .append(rule.getCouponAmount() / 100).append("元(最高返")
                    .append(rule.getMaxCouponAmount() / 100).append("元)").append(",");
        }
        return sb.length() > 0 ? sb.toString().substring(0, sb.length() - 1) : "";
    }

    @Override
    public ReturnMsg<CommonRtn> updatePromoStatus(Integer promotionId, String storeCode,
            Integer type) {
        ReturnMsg<CommonRtn> ret = new ReturnMsg<CommonRtn>();
        if (promotionId == null || type == null) {
            ret.setError(PreChargeErrorConstants.PRECHARGE_ERR_E003,
                    PreChargeErrorConstants.PRECHARGE_ERR_MAP);
        } else {
            preChargeDaoService.updateStatusByPromoId(promotionId, type);
            cacheUtils.clearCacheData(CacheKeyConstants.PRECHARGE_ACT, storeCode);
            ret.setRetFlag(ReturnMsg.SUCCESS);
        }
        return ret;
    }
}
